//
// Created by SongpingWang on 2024/12/26.
//

#ifndef NDFRAME_RE_H
#define NDFRAME_RE_H
#include <optional>
#include <iostream>
#include <regex>
#include <string>
#include <vector>
#include <sstream>
#include <iomanip>
#include <stdexcept>
#include <type_traits>
#include <numeric>
#include <map>

#ifndef MAYBE_USE
#define MAYBE_USE  [[maybe_unused]] [[nodiscard]]
#define VOID_USE   [[maybe_unused]]
#endif

template<typename CharT>
using String = std::basic_string<CharT>;

template<typename CharT>
using Regex = std::basic_regex<CharT>;

template<typename CharT>
using VecString = std::vector<std::basic_string<CharT>>;

template<typename CharT>
using MatString = std::vector<std::vector<std::basic_string<CharT>>>;

template<typename CharT>
using StringIter = typename std::basic_string<CharT>::const_iterator;

template<typename CharT>
using MatchResults = std::match_results<typename std::basic_string<CharT>::const_iterator>;

template<typename CharT>
using RegexIter = std::regex_iterator<typename std::basic_string<CharT>::const_iterator>;

template<typename CharT>
using IStrStream = std::basic_istringstream<CharT>;

template<typename CharT>
using OStrStream = std::basic_ostringstream<CharT>;

template<typename CharT>
using dict = std::map<std::basic_string<CharT>,std::basic_string<CharT>>;

template<typename CharT>
using PairStr = std::pair<std::basic_string<CharT>,std::basic_string<CharT>>;

template<typename CharT>
using VecPairStr = std::vector<std::pair<std::basic_string<CharT>,std::basic_string<CharT>>>;


namespace RE {
    template <typename CharT>
    MAYBE_USE std::optional<MatchResults<CharT>> find_first(
            const Regex<CharT>& regex_,
            const String<CharT>& text) {
        try {
            if (MatchResults<CharT> match; std::regex_search(text, match, regex_)) {
                return match;
            }
            return std::nullopt;
        } catch (const std::regex_error& e) {
            throw std::runtime_error(std::string("Regex error in find_first: ") + e.what());
        }
    }

    /**********************************************************************
     * 多捕获组：
     *      仿照python的re.findall()函数如下情形：
     *      re.compile(".*(\d{4})-(\d{1,2})-(\d{1,2}).*")
     * *********************************************************************/
    template<typename CharT>
    MAYBE_USE std::vector<VecString<CharT>> re_findall_groups(
        const Regex<CharT> regex_,
        const String<CharT> &text) {

        MatString<CharT> all_matches;
        if (regex_.mark_count() == 0) {
            std::cout << "warning regex_.mark_count() == 0" << std::endl;
            return all_matches;
        }

        try {
            RegexIter<CharT> it(text.cbegin(), text.cend(), regex_);
            RegexIter<CharT> end;

            while (it != end) {
                const MatchResults<CharT> &match = *it;
                if (match.size() > 1) {
                    VecString<CharT> current_match_groups;
                    current_match_groups.reserve(match.size() - 1);
                    for (size_t i = 1; i < match.size(); ++i) {
                        current_match_groups.push_back(match[i].str());
                    }
                    all_matches.push_back(std::move(current_match_groups));
                }
                ++it;
            }
        } catch (const std::regex_error &e) {
            throw std::runtime_error(std::string("Regex error in re_find_all_groups: ") + e.what());
        }
        return all_matches;
    }


    /**********************************************************************
    * 单捕获组：
     *      仿照python的re.findall()函数如下情形：
     *      re.compile(".*(\d+).*") 或者 re.compile("\d+")
     * *********************************************************************/
    template<typename CharT>
    VecString<CharT> re_findall_group(const String<CharT> &text, const Regex<CharT> &regex_
    ) {
        VecString<CharT> result;
        try {
            RegexIter<CharT> it(text.cbegin(), text.cend(), regex_);
            RegexIter<CharT> end;
            const unsigned int num_groups = regex_.mark_count();

            for (; it != end; ++it) {
                const MatchResults<CharT> &match = *it;
                if (num_groups > 0) {
                    if (match.size() > 1 && match[1].matched) {
                        result.push_back(match[1].str());
                    } else {
                        result.push_back({});
                    }
                } else {
                    result.push_back(match[0].str());
                }
            }
        } catch (const std::regex_error &e) {
            throw std::runtime_error(std::string("Regex error in re_findall_group: ") + e.what());
        }
        return result;
    }


    template <typename CharT>
    MAYBE_USE static VecString<CharT> split(const String<CharT>& text,  CharT sep = '#') {
        VecString<CharT> result_vector;
        if (text.empty()) { return result_vector;}

        std::basic_stringstream<CharT> ss(text);
        std::basic_string<CharT> item;

        while (std::getline(ss, item, sep)) {
            result_vector.push_back(std::move(item));
        }
        return result_vector;
    }


    template <typename CharT>
    MAYBE_USE static std::vector<int> split_to_int(const String<CharT>& text, CharT sep = '#') {
        std::vector<int> result_vector;
        if (text.empty()) {return result_vector;}

        static_assert(std::is_same_v<CharT, char> || std::is_same_v<CharT, wchar_t>,
                      "split_to_int_throwing currently only supports char and wchar_t inputs due to std::stoi limitations.");

        typename String<CharT>::size_type start = 0;
        typename String<CharT>::size_type end = text.find(sep);

        auto parse_and_add = [&](const String<CharT>& item_str_view) {
            if (item_str_view.empty()) {
                throw std::invalid_argument("Empty segment found during split_to_int");
            }
            try {
                result_vector.push_back(std::stoi(item_str_view));
            } catch (const std::invalid_argument& ia) {
                throw std::runtime_error(std::string("Failed to parse integer from segment: invalid argument '") + ia.what() + "'");
            } catch (const std::out_of_range& oor) {
                throw std::runtime_error(std::string("Failed to parse integer from segment: out of range '") + oor.what() + "'");
            }
        };

        while (end != String<CharT>::npos) {
            parse_and_add(text.substr(start, end - start));
            start = end + 1;
            if (start > text.length()) {
                if (start == text.length()){
                    parse_and_add(String<CharT>{});
                    return result_vector;
                }
                if (start > text.length()){
                    return result_vector;
                }
            }
            end = text.find(sep, start);
        }

        if (start <= text.length()) {
            parse_and_add(text.substr(start));
        }
        return result_vector;
    }


    /**********************************************************************
    * 字符串连接：
    *      仿照python的 "sep".join(List(str))函数
    * *********************************************************************/
    template<typename CharT>
    MAYBE_USE String<CharT> join(const VecString<CharT>& vec, const String<CharT>& separator) {
        if (vec.empty()) {
            return String<CharT>{};
        }

        if (vec.size() == 1) {
            return vec[0];
        }

        size_t total_size = 0;
        const size_t num_separators = vec.size() - 1;
        const size_t sep_len = separator.length();

        for (const auto& s : vec) {
            total_size += s.length();
        }
        if (num_separators > 0 && sep_len > 0) {
            total_size += num_separators * sep_len;
        }

        String<CharT> result;
        result.reserve(total_size);
        result.append(vec[0]);

        for (size_t i = 1; i < vec.size(); ++i) {
            if (sep_len > 0) {
                result.append(separator);
            }
            result.append(vec[i]);
        }

        return result;
    }

    /* 辅助函数 */
    template<typename CharT>
    void replace_all(String<CharT> &str, const String<CharT> &from, const String<CharT> &to) {
        if (from.empty()) {
            return;
        }
        size_t start = 0;
        while ((start = str.find(from, start)) != String<CharT>::npos) {
            str.replace(start, from.length(), to);
            start += to.length();
        }
    }


    /**********************************************************************
    * 替换或者移除字符串中多个字符：
    *      仿照python函数的："sep".join(List(str)).replace("¥", "_")
    *      仿照python函数的："sep".join(List(str)).replace("¥", "")
    * *********************************************************************/
    template<typename CharT>
    String<CharT> join_replace(const VecString<CharT>& vec_str,
                                      String<CharT> oldChar,
                                      String<CharT> newChar,
                                      const String<CharT> &sep = String<CharT>()) {
        if (vec_str.empty()) {
            return String<CharT>();
        }

        size_t total_len = 0;
        total_len += vec_str[0].length();
        for (size_t i = 1; i < vec_str.size(); ++i) {
            total_len += sep.length();
            total_len += vec_str[i].length();
        }

        String<CharT> result;
        result.reserve(total_len);

        result.append(vec_str[0]);
        for (size_t i = 1; i < vec_str.size(); ++i) {
            if (!sep.empty()) {
                result.append(sep);
            }
            result.append(vec_str[i]);
        }

        replace_all(result, oldChar, newChar);
        return result;
    }


    /**********************************************************************
    * 替换或者移除字符串中多个字符：
    *      仿照python函数的： str.replace("#", "_").replace("¥", "_").replace("@", "_")
    *      仿照python函数的： str.replace("#", "").replace("¥", "").replace("@", "")
    * *********************************************************************/
    template <typename CharT>
    MAYBE_USE static String<CharT> replace_multi_char(const String<CharT>& text, VecPairStr<CharT>& replace_vec)
    {
        std::sort(replace_vec.begin(), replace_vec.end(), [](const PairStr<CharT>& a, const PairStr<CharT>& b) {
            if (a.first.size() != b.first.size()) {
                return a.first.size() > b.first.size();
            }
            return a.first < b.first;
        });

        String<CharT> result = text;
        for (const auto& kv : replace_vec) {
            replace_all(result, kv.first, kv.second);
        }
        return result;
    }



    template <typename CharT>
    MAYBE_USE static std::basic_string<CharT> remove_format_date(String<CharT>& txt) {
        std::basic_string<CharT> cleaned_txt = txt;
        cleaned_txt.erase(std::remove(cleaned_txt.begin(), cleaned_txt.end(), '#'), cleaned_txt.end());
        cleaned_txt.erase(std::remove(cleaned_txt.begin(), cleaned_txt.end(), '/'), cleaned_txt.end());
        cleaned_txt.erase(std::remove(cleaned_txt.begin(), cleaned_txt.end(), '_'), cleaned_txt.end());
        cleaned_txt.erase(std::remove(cleaned_txt.begin(), cleaned_txt.end(), '-'), cleaned_txt.end());

        std::tm tm = {};
        std::basic_istringstream<CharT> ss(cleaned_txt);
        ss >> std::get_time(&tm, "%Y%m%d");

        if (ss.fail()) {
            return txt;
        }

        std::basic_ostringstream<CharT> oss;
        oss << std::put_time(&tm, "%d/%m/%Y");
        return oss.str();
    }

    template <typename CharT>
    MAYBE_USE static std::basic_string<CharT> format_date(String<CharT>& txt) {
        std::basic_istringstream<CharT> iss(txt);
        int year, month, day;
        CharT delimiter1, delimiter2;

        iss >> year >> delimiter1 >> month >> delimiter2 >> day;

        if (iss.fail() || !iss.eof()) {
            std::cerr << "Invalid date format." << std::endl;
            return txt;
        }

        std::basic_ostringstream<CharT> oss;
        oss << year << std::setw(2) << std::setfill('0') << month
            << std::setw(2) << std::setfill('0') << day;

        return oss.str();
    }
}



namespace SS {
#ifdef _WIN32
#include <windows.h>

    bool StringToWString(const std::string &str, std::wstring &wstr) {
        int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int) str.size(), nullptr, 0);
        if (size_needed == 0) {
            std::cerr << "MultiByteToWideChar failed" << std::endl;
            return false;
        }

        wstr.resize(size_needed);

        int result = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int) str.size(), &wstr[0], size_needed);
        if (result == 0) {
            std::cerr << "MultiByteToWideChar failed" << std::endl;
            return false;
        }

        return true;
    }

    bool WStringToString(const std::wstring &wstr, std::string &str) {
        int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int) wstr.size(), nullptr, 0, nullptr, nullptr);
        if (size_needed == 0) {
            std::cerr << "WideCharToMultiByte failed" << std::endl;
            return false;
        }

        str.resize(size_needed);

        int result = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int) wstr.size(), &str[0], size_needed, nullptr, nullptr);
        if (result == 0) {
            std::cerr << "WideCharToMultiByte failed" << std::endl;
            return false;
        }

        return true;
    }
#else
    inline bool WStringToString(const std::wstring& wstr, std::string& str) {
        str.resize(wstr.size() * 4, ' ');
        const size_t convertedChars = std::wcstombs(&str[0], wstr.c_str(), str.size());
        if (convertedChars == static_cast<size_t>(-1)) {
            std::cerr << "Conversion failed!" << std::endl;
            return false;
        }
        str.resize(convertedChars);
        return true;
    }

    inline bool StringToWString(const std::string& str, std::wstring& wstr) {
        wstr.resize(str.size(), L' ');
        const size_t convertedChars = std::mbstowcs(&wstr[0], str.c_str(), wstr.size());
        if (convertedChars == static_cast<size_t>(-1)) {
            std::cerr << "Conversion failed!" << std::endl;
            return false;
        }
        wstr.resize(convertedChars);
        return true;
    }

#endif
}

#endif
